import os
import threading
import time

from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt, QByteArray, QTimer, QThread, QObject, pyqtSignal, QDateTime
from PyQt5.QtGui import QTextCursor
from PyQt5.QtSerialPort import QSerialPortInfo, QSerialPort
from PyQt5.QtWidgets import QMessageBox, QWidget, QDialog, QFileDialog
from Ui_serialTools import Ui_serialTools
import binascii


class SerialTools(QWidget):
    def __init__(self):
        super(SerialTools, self).__init__()
        self.count = 0
        self.serial_tools_window = QDialog()
        self.ui_serialTools = Ui_serialTools()
        self.ui_serialTools.setupUi(self.serial_tools_window)
        self.serial_tools_window.setWindowFlags(Qt.WindowCloseButtonHint
                                                | Qt.WindowMinMaxButtonsHint
                                                )
        # 初始化串口工具
        self.serial1 = QSerialPort()
        self.serial1Timer = QTimer()
        self.serial2 = QSerialPort()
        self.serial2Timer = QTimer()
        self.available_port_name = []
        # 接收速度计算参数
        self.serialReceiveTimer = QTimer()
        self.receive_count = 0
        self.before_receiveCount = 0
        self.after_receiveCount = 0
        # 发送速度计算参数
        self.serialSendTimer = QTimer()
        self.send_count = 0
        self.before_sendCount = 0
        self.after_sendCount = 0
        # 接收分帧计时
        self.serial1_recv_frame_timer = QTimer()
        self.serial1_recv_frame_timer.setSingleShot(True)
        self.serial1_frame_time = 0
        self.serial2_recv_frame_timer = QTimer()
        self.serial2_recv_frame_timer.setSingleShot(True)
        self.serial2_frame_time = 0
        # 保存文件
        self.save_dialog = QFileDialog()
        # 初始化ui
        self.set_ui()

    def set_ui(self):
        # 关联串口刷新按钮
        self.ui_serialTools.pushButton_serial1PortRefresh.clicked.connect(self.serial1PortRefresh)
        self.ui_serialTools.pushButton_serial2PortRefresh.clicked.connect(self.serial2PortRefresh)
        # 关联清除按钮
        self.ui_serialTools.pushButton_clearreceivewin.clicked.connect(self.clear_receive_win)
        # 关联串口1发送相关的按钮
        self.serial1PortRefresh()
        self.ui_serialTools.pushButton_serial1open.clicked.connect(self.serial1_open)
        self.ui_serialTools.pushButton_serial1send.clicked.connect(self.serial1_send)
        self.serial1.readyRead.connect(self.serial1_recv_data)
        self.ui_serialTools.pushButton_serial1clear.clicked.connect(self.serial1_send_clear)
        self.ui_serialTools.checkBox_serial1istime.setChecked(False)
        self.ui_serialTools.checkBox_serial1istime.setEnabled(False)
        self.ui_serialTools.checkBox_serial1istime.stateChanged.connect(self.serial1_time_send)
        self.ui_serialTools.spinBox_serial1time.setValue(1000)
        self.serial1Timer.timeout.connect(self.serial1_send)
        self.ui_serialTools.comboBox_serial1Band.currentIndexChanged.connect(self.serial1_change_band)
        # 关联串口2
        self.serial2PortRefresh()
        self.ui_serialTools.pushButton_serial2open.clicked.connect(lambda: self.serial2_open())
        self.ui_serialTools.pushButton_serial2send.clicked.connect(self.serial2_send)
        self.serial2.readyRead.connect(self.serial2_recv_data)
        self.ui_serialTools.pushButton_serial2clear.clicked.connect(self.serial2_send_clear)
        self.ui_serialTools.checkBox_serial2istime.setChecked(False)
        self.ui_serialTools.checkBox_serial2istime.setEnabled(False)
        self.ui_serialTools.checkBox_serial2istime.stateChanged.connect(self.serial2_time_send)
        self.ui_serialTools.spinBox_serial2time.setValue(1000)
        self.serial2Timer.timeout.connect(self.serial2_send)
        # 　发送接收速度计算
        self.serialSendTimer.timeout.connect(self.send_speed_cacl)
        self.serialSendTimer.start(1000)
        self.serialReceiveTimer.timeout.connect(self.receive_speed_cacl)
        self.serialReceiveTimer.start(1000)
        # 保存文件按钮关联
        self.ui_serialTools.pushButton_savefile.clicked.connect(self.save_file)
        # 分帧读取函数
        self.serial1_recv_frame_timer.timeout.connect(self.serial1_read_all)
        self.serial2_recv_frame_timer.timeout.connect(self.serial2_read_all)

    def get_availablePorts(self):
        port_numb = QSerialPortInfo.availablePorts()
        uart_name_list = []
        uart_desc_list = []
        for info in port_numb:
            if 'COM' in info.portName():
                uart_str_name = info.portName().split()
                uart_str_desc = info.description().split()
                if uart_str_name and uart_str_desc:  # 确保列表中有元素
                    uart_name_list.append(uart_str_name[0])
                    uart_desc_list.append(uart_str_desc[0])

        # 计算实际找到的串口数量
        self.count = len(uart_name_list)

        # 如果串口数量超过256，则显示错误消息
        if self.count > 256:
            QMessageBox.critical(self, '错误', '串口太多')
        else:
            # 清空并重新填充可用串口名称列表
            self.available_port_name.clear()
            for i in range(self.count):
                # 确保列表中有元素
                if i < len(uart_name_list) and i < len(uart_desc_list):
                    self.available_port_name.append(uart_name_list[i] + ' ' + uart_desc_list[i])

    def serial1PortRefresh(self):
        # 更新串口1的串口列表
        self.get_availablePorts()
        self.ui_serialTools.comboBox_serial1.clear()
        for i in range(0, self.count):
            self.ui_serialTools.comboBox_serial1.addItem(self.available_port_name[i])

    def serial2PortRefresh(self):
        # 更新串口2的串口列表
        self.get_availablePorts()
        self.ui_serialTools.comboBox_serial2.clear()
        for i in range(0, self.count):
            self.ui_serialTools.comboBox_serial2.addItem(self.available_port_name[i])

    def get_serial_stopbits(self, current_text):
        if current_text == '1':
            return QSerialPort.StopBits.OneStop
        elif current_text == '1.5':
            return QSerialPort.StopBits.OneAndHalfStop
        elif current_text == '2':
            return QSerialPort.StopBits.TwoStop
        else:
            return QSerialPort.StopBits.UnknownStopBits

    def get_serial_databits(self, current_text):
        if current_text == '8':
            return QSerialPort.DataBits.Data8
        elif current_text == '7':
            return QSerialPort.DataBits.Data7
        elif current_text == '6':
            return QSerialPort.DataBits.Data6
        else:
            return QSerialPort.DataBits.UnknownDataBits

    def get_serial_parity(self, current_text):
        if current_text == 'None':
            return QSerialPort.Parity.NoParity
        elif current_text == 'Odd':
            return QSerialPort.Parity.OddParity
        elif current_text == 'Even':
            return QSerialPort.Parity.EvenParity
        else:
            return QSerialPort.Parity.UnknownParity

    def serial1_change_band(self):
        band = int(self.ui_serialTools.comboBox_serial1Band.currentText())
        if band <= 9600:
            self.serial1_frame_time = 10
        elif band == 115200:
            self.serial1_frame_time = 1
        elif band == 921600:
            self.serial1_frame_time = 1
        else:
            self.serial1_frame_time = 0
        self.serial1.setBaudRate(band)

    def serial1_open(self):
        bt_value = self.ui_serialTools.pushButton_serial1open.text()
        if bt_value == '打开串口':
            serial_name = self.ui_serialTools.comboBox_serial1.currentText().split(' ')[0]
            port_mes = QSerialPortInfo(serial_name)
            serial_port_state = port_mes.isBusy()
            if not serial_port_state:
                self.serial1.setPortName(serial_name)
                self.serial1_change_band()
                self.serial1.setStopBits(
                    self.get_serial_stopbits(self.ui_serialTools.comboBox_serial1Stop.currentText()))
                self.serial1.setDataBits(
                    self.get_serial_databits((self.ui_serialTools.comboBox_serial1Data.currentText())))
                self.serial1.setParity(self.get_serial_parity(self.ui_serialTools.comboBox_serial1Check.currentText()))
                self.serial1.setFlowControl(QSerialPort.FlowControl.NoFlowControl)
                if self.serial1.open(QSerialPort.ReadWrite):
                    port_mes = QSerialPortInfo(serial_name)
                    serial_port_state = port_mes.isBusy()
                    if serial_port_state:
                        self.ui_serialTools.pushButton_serial1open.setText("关闭串口")
                        self.ui_serialTools.comboBox_serial1Data.setEnabled(False)
                        self.ui_serialTools.comboBox_serial1Check.setEnabled(False)
                        self.ui_serialTools.comboBox_serial1Stop.setEnabled(False)
                        # self.ui_serialTools.comboBox_serial1Band.setEnabled(False)
                        self.ui_serialTools.comboBox_serial1.setEnabled(False)
                        self.ui_serialTools.checkBox_serial1istime.setEnabled(True)
                        # 刷新页面防止假死
                        QtWidgets.QApplication.processEvents()
                else:
                    QMessageBox.warning(self, 'Error', '打开串口1时出现了' + self.serial1.errorString())


            else:
                QMessageBox.warning(self, '错误', '串口已被打开')
        elif bt_value == '关闭串口':
            if not self.ui_serialTools.checkBox_serial1istime.isChecked():
                self.serial1.close()
                self.ui_serialTools.pushButton_serial1open.setText("打开串口")
                self.ui_serialTools.comboBox_serial1Data.setEnabled(True)
                self.ui_serialTools.comboBox_serial1Check.setEnabled(True)
                self.ui_serialTools.comboBox_serial1Stop.setEnabled(True)
                # self.ui_serialTools.comboBox_serial1Band.setEnabled(True)
                self.ui_serialTools.comboBox_serial1.setEnabled(True)
                self.ui_serialTools.checkBox_serial1istime.setEnabled(False)
            else:
                QMessageBox.warning(self, '错误', '请关闭定时发送')
        else:
            pass

    def serial1_recv_data(self):
        self.serial1_recv_frame_timer.stop()
        self.serial1_recv_frame_timer.start(self.serial1_frame_time)

    def serial1_read_all(self):
        self.serial1_recv_frame_timer.stop()
        data = self.serial1.readAll()
        self.receive_count += data.length()
        self.ui_serialTools.label_receivecount.setText(str(self.receive_count))
        if not data.isEmpty():
            if self.ui_serialTools.checkBox_isHex.isChecked():
                # 如果选择了hex格式，就转hex
                msg_temp1 = data.toHex(' ').toUpper().data()
                msg = str(msg_temp1, encoding='utf-8')
            else:
                # 否则转str
                msg_temp = data.data()
                msg = str(msg_temp, encoding='utf-8')
            if self.ui_serialTools.checkBox_time.isChecked():
                # 如果选择了时间戳，就加上时间
                current_time = QDateTime.currentDateTime().toString("hh:mm:ss:zzz")
                msg_ready = '[' + current_time + ']' + msg + '\r\n'
            else:
                msg_ready = msg
            if self.ui_serialTools.checkBox_isstopdisplay.isChecked():
                pass
            else:
                self.ui_serialTools.textBrowser.moveCursor(QTextCursor.End)
                self.ui_serialTools.textBrowser.append("<font color=\"#ffa39e\">" + msg_ready + "</font>")

    def clear_receive_win(self):
        self.ui_serialTools.textBrowser.clear()
        self.receive_count = 0
        self.ui_serialTools.label_receivecount.setText(str(self.receive_count))
        self.send_count = 0
        self.ui_serialTools.label_sendcount.setText(str(self.send_count))

    def serial1_send(self):
        if self.serial1.isOpen():
            input_s = self.ui_serialTools.textBrowser_send1text.toPlainText()
            if input_s != "":
                if self.ui_serialTools.checkBox_serial1ishex.isChecked():
                    input_s = input_s.strip()
                    send_list = []
                    while input_s != '':
                        try:
                            num = int(input_s[0:2], 16)
                        except:
                            QMessageBox.warning(self, "数据格式不对", "请输入16进制数据")
                            return None
                        input_s = input_s[2:].strip()
                        send_list.append(num)
                    if self.ui_serialTools.checkBox_serial1isenter.isChecked():
                        send_list.append(0x0D)
                        send_list.append(0x0A)
                        serial1_input_end = bytes(send_list)
                    else:
                        serial1_input_end = bytes(send_list)
                else:
                    if self.ui_serialTools.checkBox_serial1isenter.isChecked():
                        serial1_input_end = (input_s + '\r\n').encode('utf-8')
                    else:
                        serial1_input_end = input_s.encode('utf-8')
                num = self.serial1.write(serial1_input_end)
                self.send_count += num
                self.ui_serialTools.label_sendcount.setText(str(self.send_count))
            else:
                QMessageBox.warning(self, "发送区为空", "请输入发送数据")
        else:
            QMessageBox.warning(self, "串口未打开", "请打开串口")

            pass

    def serial1_send_clear(self):
        self.ui_serialTools.textBrowser_send1text.clear()

    def serial1_time_send(self):
        if self.ui_serialTools.textBrowser_send1text.toPlainText() != "":
            if self.serial1.isOpen():
                if self.ui_serialTools.checkBox_serial1istime.isChecked():
                    time_count = self.ui_serialTools.spinBox_serial1time.value()
                    self.serial1Timer.start(time_count)
                    self.ui_serialTools.pushButton_serial1send.setEnabled(False)
                else:
                    self.serial1Timer.stop()
                    self.ui_serialTools.pushButton_serial1send.setEnabled(True)
            else:
                QMessageBox.warning(self, "串口未打开", "请打开串口")
        else:
            QMessageBox.warning(self, "发送区为空", "请输入发送数据")
            self.ui_serialTools.checkBox_serial1istime.setChecked(False)
            pass

    def serial2_open(self):
        bt_value = self.ui_serialTools.pushButton_serial2open.text()
        if bt_value == '打开串口':
            serial_name = self.ui_serialTools.comboBox_serial2.currentText().split(' ')[0]
            port_mes = QSerialPortInfo(serial_name)
            serial_port_state = port_mes.isBusy()
            if not serial_port_state:
                self.serial2.setPortName(serial_name)
                band = int(self.ui_serialTools.comboBox_serial2Band.currentText())
                if band <= 9600:
                    self.serial2_frame_time = 10
                elif band == 115200:
                    self.serial2_frame_time = 3
                elif band == 921600:
                    self.serial2_frame_time = 1
                else:
                    self.serial2_frame_time = 0
                self.serial2.setBaudRate(band)
                self.serial2.setStopBits(
                    self.get_serial_stopbits(self.ui_serialTools.comboBox_serial2Stop.currentText()))
                self.serial2.setDataBits(
                    self.get_serial_databits((self.ui_serialTools.comboBox_serial2Data.currentText())))
                self.serial2.setParity(self.get_serial_parity(self.ui_serialTools.comboBox_serial2Check.currentText()))
                self.serial2.setFlowControl(QSerialPort.FlowControl.NoFlowControl)
                try:
                    self.serial2.open(QSerialPort.ReadWrite)
                    port_mes = QSerialPortInfo(serial_name)
                    serial_port_state = port_mes.isBusy()
                    if serial_port_state:
                        self.ui_serialTools.pushButton_serial2open.setText("关闭串口")
                        self.ui_serialTools.comboBox_serial2Data.setEnabled(False)
                        self.ui_serialTools.comboBox_serial2Check.setEnabled(False)
                        self.ui_serialTools.comboBox_serial2Stop.setEnabled(False)
                        # self.ui_serialTools.comboBox_serial2Band.setEnabled(False)
                        self.ui_serialTools.comboBox_serial2.setEnabled(False)
                        self.ui_serialTools.checkBox_serial2istime.setEnabled(True)
                except:
                    QMessageBox.warning(self, '错误', '串口打开失败')

            else:
                QMessageBox.warning(self, '错误', '串口已被打开')
        elif bt_value == '关闭串口':
            if not self.ui_serialTools.checkBox_serial2istime.isChecked():
                self.serial2.close()
                self.ui_serialTools.pushButton_serial2open.setText("打开串口")
                self.ui_serialTools.comboBox_serial2Data.setEnabled(True)
                self.ui_serialTools.comboBox_serial2Check.setEnabled(True)
                self.ui_serialTools.comboBox_serial2Stop.setEnabled(True)
                # self.ui_serialTools.comboBox_serial2Band.setEnabled(True)
                self.ui_serialTools.comboBox_serial2.setEnabled(True)
                self.ui_serialTools.checkBox_serial2istime.setEnabled(False)
                self.ui_serialTools.checkBox_serial2istime.setChecked(False)
            else:
                QMessageBox.warning(self, '错误', '请关闭定时发送')
        else:
            pass

    def serial2_recv_data(self):
        self.serial1_recv_frame_timer.stop()
        self.serial2_recv_frame_timer.start(self.serial2_frame_time)

    def serial2_read_all(self):
        self.serial1_recv_frame_timer.stop()
        data = self.serial2.readAll()
        self.receive_count += data.length()
        self.ui_serialTools.label_receivecount.setText(str(self.receive_count))
        if not data.isEmpty():
            if self.ui_serialTools.checkBox_isHex.isChecked():
                # 如果选择了hex格式，就转hex
                msg_temp1 = data.toHex(' ').toUpper().data()
                msg = str(msg_temp1, encoding='utf-8')
            else:
                # 否则转str
                msg_temp = data.data()
                msg = str(msg_temp, encoding='utf-8')
            if self.ui_serialTools.checkBox_time.isChecked():
                # 如果选择了时间戳，就加上时间
                current_time = QDateTime.currentDateTime().toString("hh:mm:ss:zzz")
                msg_ready = '[' + current_time + ']' + msg + '\r\n'
            else:
                msg_ready = msg
            if self.ui_serialTools.checkBox_isstopdisplay.isChecked():
                pass
            else:
                self.ui_serialTools.textBrowser.moveCursor(QTextCursor.End)
                self.ui_serialTools.textBrowser.append("<font color=\"#adc6ff\">" + msg_ready + "</font>")

    def serial2_send(self):
        if self.serial2.isOpen():
            input_s = self.ui_serialTools.textBrowser_send2text.toPlainText()
            if input_s != "":
                if self.ui_serialTools.checkBox_serial2ishex.isChecked():
                    input_s = input_s.strip()
                    send_list = []
                    while input_s != '':
                        try:
                            num = int(input_s[0:2], 16)
                        except:
                            QMessageBox.warning(self, "数据格式不对", "请输入16进制数据")
                            return None
                        input_s = input_s[2:].strip()
                        send_list.append(num)
                    if self.ui_serialTools.checkBox_serial2isenter.isChecked():
                        send_list.append(0x0D)
                        send_list.append(0x0A)
                        serial2_input_end = bytes(send_list)
                    else:
                        serial2_input_end = bytes(send_list)
                else:
                    if self.ui_serialTools.checkBox_serial2isenter.isChecked():
                        serial2_input_end = (input_s + '\r\n').encode('utf-8')
                    else:
                        serial2_input_end = input_s.encode('utf-8')
                num = self.serial2.write(serial2_input_end)
                self.send_count += num
                self.ui_serialTools.label_sendcount.setText(str(self.send_count))
            else:
                QMessageBox.warning(self, "发送区为空", "请输入发送数据")
        else:
            QMessageBox.warning(self, "串口未打开", "请打开串口")
            pass

    def serial2_send_clear(self):
        self.ui_serialTools.textBrowser_send2text.clear()

    def serial2_time_send(self):
        if self.ui_serialTools.textBrowser_send2text.toPlainText() != "":
            if self.serial2.isOpen():
                if self.ui_serialTools.checkBox_serial2istime.isChecked():
                    time_count = self.ui_serialTools.spinBox_serial2time.value()
                    self.serial2Timer.start(time_count)
                    self.ui_serialTools.pushButton_serial2send.setEnabled(False)
                else:
                    self.serial2Timer.stop()
                    self.ui_serialTools.pushButton_serial2send.setEnabled(True)
            else:
                QMessageBox.warning(self, "串口未打开", "请打开串口")
        else:
            QMessageBox.warning(self, "发送区为空", "请输入发送数据")
            self.ui_serialTools.checkBox_serial2istime.setChecked(False)
            pass

    def send_speed_cacl(self):
        self.after_sendCount = self.send_count
        send_speed = self.after_sendCount - self.before_sendCount
        if send_speed < 0:
            send_speed = 0
        self.ui_serialTools.label_sendcount_speed.setText(str(send_speed))
        self.before_sendCount = self.after_sendCount

    def receive_speed_cacl(self):
        self.after_receiveCount = self.receive_count
        receive_speed = self.after_receiveCount - self.before_receiveCount
        if receive_speed < 0:
            receive_speed = 0
        self.ui_serialTools.label_receivecount_speed.setText(str(receive_speed))
        self.before_receiveCount = self.after_receiveCount

    def save_file(self):
        save_path, _ = self.save_dialog.getSaveFileName(self, "另存为", os.getcwd(), "Text files (*.txt)")
        if save_path:
            with open(save_path, "w") as f:
                str_text = self.ui_serialTools.textBrowser.toPlainText()
                qs = str(str_text)
                f.write('\n{}'.format(qs))
                f.close()
